package location;

import static java.util.Arrays.asList;
import static java.util.regex.Pattern.quote;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import item.ItemProvider;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

import main.CreatureTemplate;
import main.LootContainer;
import main.Trap;
import monster.Monster;
import serialize.LocationAdapter;

import com.item.manager.repository.TemplateRepository;

import encounter.Encounter;
import encounter.NatureSeachEncounter;
import encounter.UniqueMonsterEncounter;

/**
 * LocationAdapter to Location instance.
 *
 * @author Blind
 */
public class ToLocationFunction implements Function<LocationAdapter, Location> {
    private final TemplateRepository monsterRepo;
    private final TemplateRepository trapRepo;
    private final TemplateRepository lootRepo;
    private final ItemProvider itemProvider;

    public ToLocationFunction(TemplateRepository monsterRepo, TemplateRepository trapRepo,
                              TemplateRepository lootRepo, ItemProvider itemProvider) {
        this.monsterRepo = monsterRepo;
        this.trapRepo = trapRepo;
        this.lootRepo = lootRepo;
        this.itemProvider = itemProvider;
    }

    public Location apply(LocationAdapter adapter) {
        String name = adapter.getName();

        List<EncounterConfig> configs = adapter.getEncounters().stream().
                map(e -> TYPES.stream().filter(t -> t.match(e)).findFirst().
                        map(o -> o.fromString(e)).get()).collect(toList());

        Map<Encounter, Integer> collect = configs.stream().collect(toMap(e -> e.encounter, e -> e.size));

        return new BasicLocation(name, collect, Optional.<Location>empty());
    }

    EncounterType MONSTER = new EncounterType("Monster") {
        EncounterConfig fromString(String s) {
            CreatureTemplate template = new CreatureTemplate(monsterRepo.byName(name(s)));
            return new EncounterConfig(template, count(s));
        }
    };

    EncounterType ITEM = new EncounterType("Item") {
        EncounterConfig fromString(String s) {
            return new EncounterConfig(new NatureSeachEncounter(itemProvider, lootRepo.byName(name(s))), count(s));
        }
    };

    EncounterType LOOT = new EncounterType("Loot") {
        EncounterConfig fromString(String s) {
            return new EncounterConfig(new LootContainer(lootRepo.byName(name(s))), count(s));
        }
    };

    EncounterType UNIQUE_MONSTER = new EncounterType("UniqueMonster") {
        EncounterConfig fromString(String s) {
            UniqueMonsterEncounter template = new UniqueMonsterEncounter(
                    new Monster(new CreatureTemplate(monsterRepo.byName(name(s)))));
            return new EncounterConfig(template, count(s));
        }
    };

    EncounterType TRAP = new EncounterType("Trap") {
        EncounterConfig fromString(String s) {
            Trap template = new Trap(trapRepo.byName(name(s)));
            return new EncounterConfig(template, count(s));
        }
    };

    List<EncounterType> TYPES = asList(MONSTER, ITEM, LOOT, UNIQUE_MONSTER, TRAP);


    class EncounterConfig {
        final Encounter encounter;
        final int size;

        public EncounterConfig(Encounter encounter, int size) {
            this.encounter = encounter;
            this.size = size;
        }
    }

    class EncounterType {

        public String name;

        EncounterType(String name) {
            this.name = name;
        }

        boolean match(String s) {
            return s.startsWith(name);
        }

        int count(String s) {
            return Integer.valueOf(split(s)[1].split(":")[1].trim());
        }

        private String[] split(String s) {
            return s.split(quote(","));
        }

        String name(String s) {
            String[] a = split(s);
            return a[0].split(":")[1];
        }

        EncounterConfig fromString(String s) {
            throw new RuntimeException("not implemented");
        }
    }
}
